package algebra.rewriting;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.HashSet;

import java.util.Set;

import kb.DescriptionLogicsKB;

import algebra.cost.PlanEnumeration;
import algebra.op.AlgebraicOP;
import algebra.query.indexing.SecondaryIndex;

/**
 * Remember to place an entry for each impl class in
 * META-INF/services/some-flat-file
 * 
 * @author j55wu
 * 
 */
public class RuleService {
	// note that the path is relative to the current class path, as defined in
	// System.getProperty("java.class.path")
	private static String ruleSeriveLocation = "META-INF/services";
	private static String ruleInterfaceName = "algebra.rewriting.RewriteRule";
	private static String[] ruleNames = {
			"algebra.rewriting.relational.CacheIntro",
			"algebra.rewriting.relational.NestedSearch",
			"algebra.rewriting.relational.RemoveProjection",
			"algebra.rewriting.relational.RemoveSelection",
			"algebra.rewriting.kfree.KFreeRewriting",
			"algebra.rewriting.transform.AssociativeIntersectionF",
			"algebra.rewriting.transform.AssociativeIntersectionS",
			"algebra.rewriting.transform.CommutativeIntersection",
			"algebra.rewriting.transform.CommutativeSelection",
			"algebra.rewriting.transform.DecompSelection",
			"algebra.rewriting.transform.IntersectionInsideSelection",
			"algebra.rewriting.transform.MergeProjection",
			"algebra.rewriting.transform.MergeSelection",
			"algebra.rewriting.transform.ScanSelection", };

	// private static boolean entriesCreated = false;

	// w/o using resource finder
	public static Set<RewriteRule> getRulePlain(AlgebraicOP op,
			Set<SecondaryIndex> sis, PlanEnumeration pe) {
		Set<RewriteRule> res = new HashSet<RewriteRule>();
		DescriptionLogicsKB kb = pe.getKB();
		for (String s : ruleNames) {
			try {
				Class<?> c = Class.forName(s);
				Constructor<?> con = c.getConstructors()[0];
				Class<?>[] args = con.getParameterTypes();
				Object[] feed = new Object[args.length];
				for (int i = 0; i < args.length; i++) {
					if (args[i].equals(AlgebraicOP.class)) {
						feed[i] = op;
					} else if (args[i].equals(Set.class)) {
						feed[i] = sis;
					} else if (args[i].equals(DescriptionLogicsKB.class)) {
						feed[i] = kb;
					} else if (args[i].equals(PlanEnumeration.class)) {
						feed[i] = pe;
					}
				}
				RewriteRule rule = (RewriteRule) con.newInstance(feed);
				if (rule.applicable()) {
					res.add(rule);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return res;
	}

	// possible args to feed in rules: op, SIs, KB
	// public static Set<RewriteRule> getRule(AlgebraicOP op,
	// Set<SecondaryIndex> sis, DescriptionLogicsKB kb){
	// if (!entriesCreated){
	// createServiceEntries();
	// entriesCreated = true;
	// }
	// Set<RewriteRule> res = new HashSet<RewriteRule>();
	// ResourceFinder finder = new ResourceFinder(ruleSeriveLocation);
	// try {
	// List<Class<? extends RewriteRule>> impls =
	// finder.findAllImplementations(algebra.rewriting.RewriteRule.class);
	// for(Class<? extends RewriteRule> c:impls){
	// Constructor<?> con = c.getConstructors()[0];
	// Class<?>[] args = con.getParameterTypes();
	// Object[] feed = new Object[args.length];
	// for(int i=0; i<args.length; i++){
	// if(args[i].equals(AlgebraicOP.class)){
	// feed[i] = op;
	// }else if(args[i].equals(Set.class)){
	// feed[i] = sis;
	// }else if(args[i].equals(DescriptionLogicsKB.class)){
	// feed[i] = kb;
	// }
	// }
	// RewriteRule rule = (RewriteRule) con.newInstance(feed);
	// if(rule.applicable()){
	// res.add(rule);
	// }
	// }
	// } catch (Exception e) {
	// e.printStackTrace();
	// }
	// return res;
	// }

	public static void createServiceEntries() {
		String path = System.getProperty("java.class.path");
		String dir = null;
		int sep = path.indexOf(":");
		if (sep <= 0) {
			// only one path given
			dir = path;
		} else {
			dir = path.substring(0, sep);
		}
		// System.err.println("Service entries to be found in "+dir);
		// start building
		dir += "/" + ruleSeriveLocation;
		File loc = new File(dir);
		loc.mkdirs();
		dir += "/" + ruleInterfaceName;
		loc = new File(dir);
		if (loc.exists()) {
			return;
		}
		// write into the file
		try {
			FileWriter wt = new FileWriter(loc);
			for (String rule : ruleNames) {
				wt.write(rule + System.getProperty("line.separator"));
			}
			wt.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}
